package raft

// example RequestVote RPC arguments structure.
// field names must start with capital letters!

type RequestVoteArgs struct {
	// Your data here (2A, 2B).
	Term        int
	CandidateId int
}

type RequestVoteReply struct {
	// Your data here (2A).
	CurrentTerm int
	VoteGranted bool
	Replyer     int
	Replyinfo   string
}

// example RequestVote RPC handler.
func (rf *Raft) RequestVote(args *RequestVoteArgs, reply *RequestVoteReply) {
	// Your code here (2A, 2B).
	// 2A
	/*
		1)term < currentterm ,return false
		2)term >= currentTerm & (votefor = null or votefor == candidateid) ,vote
		3)else reject
	*/
	rf.mu.Lock()
	defer rf.mu.Unlock()
	// for the acceptor
	reply.Replyer = rf.me
	if args.Term < rf.currentTerm {
		reply.VoteGranted = false
		reply.Replyinfo = "refuse : small term"
		reply.CurrentTerm = rf.currentTerm
		return
	}
	if args.Term > rf.currentTerm {
		rf.currentTerm = args.Term
		reply.CurrentTerm = rf.currentTerm
		rf.state = FOLLOWER
		rf.voteFor = -1
	}
	if args.Term == rf.currentTerm {
		if rf.voteFor != -1 && rf.voteFor != args.CandidateId {
			reply.VoteGranted = false
			reply.Replyinfo = "refuse : vote for other"
			reply.CurrentTerm = rf.currentTerm
			return

		} else {
			reply.VoteGranted = true
			reply.Replyinfo = "agree"
			reply.CurrentTerm = rf.currentTerm
			rf.state = FOLLOWER
			rf.voteFor = args.CandidateId

		}
	}
	//promise only a leader , begin differently
	//once become a follower , start a timer
	rf.ElectionTimer.Reset(randomElctionTimeout())

}

// Call() is guaranteed to return (perhaps after a delay) *except* if the
// handler function on the server side does not return.  Thus there
// is no need to implement your own timeouts around Call().
func (rf *Raft) sendRequestVote(server int, args *RequestVoteArgs, reply *RequestVoteReply) bool {
	ok := rf.peers[server].Call("Raft.RequestVote", args, reply)
	/*
		fmt.Println(
			"requester: ", args.CandidateId, "term", args.Term, "\n",
			"replyer: ", reply.Replyer, " reply: ", reply.VoteGranted, " term: ", reply.CurrentTerm, "\n",
			"info:", reply.Replyinfo,
		)
	*/
	return ok
}

//========================================================================
// term > current_term ; false
// update term , state->follower

type AppendEntriesArgs struct {
	Term         int
	LeaderId     int
	PrevLogIndex int //before the newone
	PrevLogTerm  int
	entries      []byte //stor(empty for heatbeat)
	LeaderCommit int    //leader's commitIndex
}
type AppendEntriesReply struct {
	Success bool
	Term    int
}

func (rf *Raft) AppendEntries(args *AppendEntriesArgs, reply *AppendEntriesReply) {
	rf.mu.Lock()
	rf.mu.Unlock()
	if args.Term < rf.currentTerm {
		reply.Success = false
		reply.Term = rf.currentTerm
		return
	}

	if args.Term > rf.currentTerm {
		rf.currentTerm = args.Term
		rf.voteFor = -1
		rf.state = FOLLOWER
	}
	reply.Success = true
	reply.Term = rf.currentTerm
	rf.ElectionTimer.Reset(randomElctionTimeout())

}

func (rf *Raft) sendAppendEntries(server int, args *AppendEntriesArgs, reply *AppendEntriesReply) bool {
	ok := rf.peers[server].Call("Raft.AppendEntries", args, reply)
	return ok
}
